package com.wondertek.poms.starter.logparent;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wondertek.poms.common.constant.Constants;
import com.wondertek.poms.common.controller.BaseController;
import com.wondertek.poms.common.log.MethodLog;
import com.wondertek.poms.dao.po.PcOperateLog;

import lombok.extern.slf4j.Slf4j;

/**
 * <p><b>LogAop Description:</b> aop实现日志</p>
 *
 * @author douzi
 * <b>DATE</b> 2019年8月12日 下午7:05:30
 */
@Aspect
@Component
@Slf4j
public class LogAop {
    private static BaseController baseController = new BaseController();

    @Autowired
	private AmqpTemplate amqpTemplate;
    
    LogAop() {
        log.info("==============初始化LogAop.java================");
    }

    /**
     * 环绕带注解 @MethodLog的方法做aop
     */
    @Around(value = "@annotation(com.wondertek.poms.common.log.MethodLog)")
    public Object logSave(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        PcOperateLog olog = new PcOperateLog();
        PcOperateLog.putTimeAndUser(olog);
        olog.setIp(request.getRemoteAddr());
        olog.setUrl(request.getRequestURI());
        /*request.getQueryString();
        String str = request.getQueryString();
        BufferedReader bufferedReader = request.getReader();
        String bodyStr = IOUtils.read(bufferedReader);*/

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        MethodLog methodLog = methodSignature.getMethod().getDeclaredAnnotation(MethodLog.class);
        String methodRemark = methodLog.module();
        int methodOpterate = methodLog.opterate();
        boolean insertDB = methodLog.insertDB();
        olog.setModel(methodRemark);
        olog.setOperateType(methodOpterate);

        String params = getParams(joinPoint, methodSignature, methodLog);
        olog.setParams(params);

        try {
            Object object = joinPoint.proceed();// 执行原方法
            log.info(baseController.getOptLog(request, methodRemark,
                    joinPoint.getTarget().getClass().getSimpleName() + ":" + joinPoint.getSignature().getName(), "1",
                    params));
            olog.setExceptionFlag(Boolean.FALSE);

            // 当新增的时候
            if (methodOpterate == 1 && methodLog.recordParam() && object != null) {
            	olog.setParams(JSONObject.toJSONString(object));
            }
            // 当编辑的时候
            if (methodOpterate == 2 && methodLog.recordParam() && object != null) {
                olog.setParams(JSONObject.toJSONString(params));
            }

            // 当删除的时候
            if (methodOpterate == 3 && methodLog.recordParam() && object != null) {
                olog.setParams(JSONObject.toJSONString(params));
            }


            return object;
        } catch (Exception e) { // 方法执行失败
            log.error(baseController.getOptLog(request, methodRemark,
                    joinPoint.getTarget().getClass().getSimpleName() + ":" + joinPoint.getSignature().getName(), "0",
                    params + "|" + e.getMessage()));
            olog.setExceptionFlag(Boolean.TRUE);
            throw e;
        } finally {
            if (insertDB) {
                // 异步将Log对象发送
                CompletableFuture.runAsync(() -> {
                    try {
                    	amqpTemplate.convertAndSend(Constants.LOG_QUEUE, JSON.toJSONString(olog));
                        log.info("发送日志到日志系统:{}", olog);
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    }
                });
            }
        }
    }

    /**
     * <p><b>Title:</b> getParams</p>
     * <p><b>Description:</b> 获取参数集合</p>
     *
     * @param joinPoint
     * @param methodLog
     * @param methodSignature
     * @param methodLog
     * @author douzi
     */
    private String getParams(ProceedingJoinPoint joinPoint, MethodSignature methodSignature,
                             MethodLog methodLog) {
        if (methodLog.recordParam()) { // 是否要记录方法的参数数据
            String[] paramNames = methodSignature.getParameterNames();// 参数名

            if (paramNames != null && paramNames.length > 0) {
                Object[] args = joinPoint.getArgs();// 参数值

                Map<String, Object> params = new HashMap<>();
                for (int i = 0; i < paramNames.length; i++) {
                    Object value = args[i];
                    if (value instanceof Serializable) {
                        params.put(paramNames[i], value);
                    }
                }

                try {
                    return JSONObject.toJSONString(params); // 以json的形式记录参数
                } catch (Exception e) {
                    log.error("记录参数失败:{}", e.getMessage());
                }
            }
        }
        return "";
    }
}
